"""
文档转换服务
负责处理Markdown到各种格式的转换逻辑
"""

import os
import subprocess
import tempfile
import logging
from typing import Dict, Any, Optional, List
from pathlib import Path
import pypandoc
from weasyprint import HTML, CSS
from jinja2 import Environment, FileSystemLoader, select_autoescape
from ..models.task import Task
from ..models.template import Template
from ..utils.file_utils import FileUtils
from ..utils.validation_utils import ValidationUtils

logger = logging.getLogger(__name__)

class ConversionService:
    """文档转换服务类"""
    
    def __init__(self, app=None):
        self.app = app
        self.supported_formats = ['pdf', 'docx', 'html', 'epub', 'rtf', 'odt']
        self.pandoc_formats = ['docx', 'html', 'epub', 'rtf', 'odt']
        self.weasyprint_formats = ['pdf']
        
        # 初始化Jinja2模板环境
        template_dir = app.config.get('TEMPLATE_DIR', 'templates') if app else 'templates'
        self.jinja_env = Environment(
            loader=FileSystemLoader(template_dir),
            autoescape=select_autoescape(['html', 'xml'])
        )
    
    def convert_document(self, task_id: str, input_file: str, output_format: str, 
                        template_id: Optional[str] = None, options: Optional[Dict] = None) -> Dict[str, Any]:
        """
        转换文档
        
        Args:
            task_id: 任务ID
            input_file: 输入文件路径
            output_format: 输出格式
            template_id: 模板ID（可选）
            options: 转换选项（可选）
            
        Returns:
            转换结果字典
        """
        try:
            logger.info(f"开始转换任务 {task_id}: {input_file} -> {output_format}")
            
            # 验证输入文件
            if not os.path.exists(input_file):
                raise FileNotFoundError(f"输入文件不存在: {input_file}")
            
            # 验证输出格式
            if output_format not in self.supported_formats:
                raise ValueError(f"不支持的输出格式: {output_format}")
            
            # 读取输入文件内容
            with open(input_file, 'r', encoding='utf-8') as f:
                content = f.read()
            
            # 验证Markdown内容
            if not ValidationUtils.validate_markdown_content(content):
                raise ValueError("无效的Markdown内容")
            
            # 获取模板配置
            template_config = None
            if template_id:
                template = Template.find_by_id(template_id)
                if template:
                    template_config = template.get('config', {})
            
            # 合并转换选项
            conversion_options = self._merge_options(template_config, options)
            
            # 生成输出文件路径
            output_file = self._generate_output_path(task_id, output_format)
            
            # 执行转换
            if output_format in self.pandoc_formats:
                result = self._convert_with_pandoc(content, output_file, output_format, conversion_options)
            elif output_format in self.weasyprint_formats:
                result = self._convert_with_weasyprint(content, output_file, conversion_options)
            else:
                raise ValueError(f"未实现的转换格式: {output_format}")
            
            # 验证输出文件
            if not os.path.exists(output_file):
                raise RuntimeError("转换失败：输出文件未生成")
            
            file_size = os.path.getsize(output_file)
            if file_size == 0:
                raise RuntimeError("转换失败：输出文件为空")
            
            logger.info(f"转换完成 {task_id}: {output_file} ({file_size} bytes)")
            
            return {
                'success': True,
                'output_file': output_file,
                'file_size': file_size,
                'format': output_format,
                'options_used': conversion_options
            }
            
        except Exception as e:
            logger.error(f"转换失败 {task_id}: {str(e)}")
            return {
                'success': False,
                'error': str(e),
                'error_type': type(e).__name__
            }
    
    def _convert_with_pandoc(self, content: str, output_file: str, 
                           output_format: str, options: Dict) -> Dict[str, Any]:
        """使用Pandoc进行转换"""
        try:
            # 准备Pandoc参数
            extra_args = self._build_pandoc_args(output_format, options)
            
            # 执行转换
            pypandoc.convert_text(
                content,
                output_format,
                format='markdown',
                outputfile=output_file,
                extra_args=extra_args
            )
            
            return {'method': 'pandoc', 'args': extra_args}
            
        except Exception as e:
            logger.error(f"Pandoc转换失败: {str(e)}")
            raise RuntimeError(f"Pandoc转换失败: {str(e)}")
    
    def _convert_with_weasyprint(self, content: str, output_file: str, 
                               options: Dict) -> Dict[str, Any]:
        """使用WeasyPrint进行PDF转换"""
        try:
            # 将Markdown转换为HTML
            html_content = pypandoc.convert_text(content, 'html', format='markdown')
            
            # 应用模板
            if options.get('template'):
                html_content = self._apply_template(html_content, options['template'])
            
            # 准备CSS样式
            css_styles = self._build_css_styles(options)
            
            # 创建临时HTML文件
            with tempfile.NamedTemporaryFile(mode='w', suffix='.html', delete=False, encoding='utf-8') as temp_html:
                temp_html.write(html_content)
                temp_html_path = temp_html.name
            
            try:
                # 使用WeasyPrint转换为PDF
                html_doc = HTML(filename=temp_html_path)
                if css_styles:
                    css_doc = CSS(string=css_styles)
                    html_doc.write_pdf(output_file, stylesheets=[css_doc])
                else:
                    html_doc.write_pdf(output_file)
                
                return {'method': 'weasyprint', 'css_applied': bool(css_styles)}
                
            finally:
                # 清理临时文件
                os.unlink(temp_html_path)
                
        except Exception as e:
            logger.error(f"WeasyPrint转换失败: {str(e)}")
            raise RuntimeError(f"WeasyPrint转换失败: {str(e)}")
    
    def _build_pandoc_args(self, output_format: str, options: Dict) -> List[str]:
        """构建Pandoc命令行参数"""
        args = []
        
        # 基本参数
        if output_format == 'docx':
            args.extend(['--standalone'])
        elif output_format == 'html':
            args.extend(['--standalone', '--self-contained'])
        elif output_format == 'epub':
            args.extend(['--epub-cover-image'])
        
        # 自定义参数
        if options.get('toc'):
            args.append('--toc')
        
        if options.get('number_sections'):
            args.append('--number-sections')
        
        if options.get('highlight_style'):
            args.extend(['--highlight-style', options['highlight_style']])
        
        # 模板文件
        if options.get('template_file'):
            args.extend(['--template', options['template_file']])
        
        # 变量设置
        if options.get('variables'):
            for key, value in options['variables'].items():
                args.extend(['-V', f'{key}={value}'])
        
        return args
    
    def _build_css_styles(self, options: Dict) -> str:
        """构建CSS样式"""
        css_parts = []
        
        # 基础样式
        css_parts.append("""
        body {
            font-family: 'Times New Roman', serif;
            line-height: 1.6;
            margin: 2cm;
            color: #333;
        }
        h1, h2, h3, h4, h5, h6 {
            color: #2c3e50;
            margin-top: 1.5em;
            margin-bottom: 0.5em;
        }
        code {
            background-color: #f8f9fa;
            padding: 0.2em 0.4em;
            border-radius: 3px;
            font-family: 'Courier New', monospace;
        }
        pre {
            background-color: #f8f9fa;
            padding: 1em;
            border-radius: 5px;
            overflow-x: auto;
        }
        blockquote {
            border-left: 4px solid #3498db;
            padding-left: 1em;
            margin-left: 0;
            font-style: italic;
        }
        table {
            border-collapse: collapse;
            width: 100%;
            margin: 1em 0;
        }
        th, td {
            border: 1px solid #ddd;
            padding: 0.5em;
            text-align: left;
        }
        th {
            background-color: #f2f2f2;
        }
        """)
        
        # 自定义样式
        if options.get('font_size'):
            css_parts.append(f"body {{ font-size: {options['font_size']}; }}")
        
        if options.get('font_family'):
            css_parts.append(f"body {{ font-family: {options['font_family']}; }}")
        
        if options.get('margin'):
            css_parts.append(f"body {{ margin: {options['margin']}; }}")
        
        if options.get('custom_css'):
            css_parts.append(options['custom_css'])
        
        return '\n'.join(css_parts)
    
    def _apply_template(self, html_content: str, template_name: str) -> str:
        """应用HTML模板"""
        try:
            template = self.jinja_env.get_template(f'{template_name}.html')
            return template.render(content=html_content)
        except Exception as e:
            logger.warning(f"模板应用失败: {str(e)}")
            return html_content
    
    def _merge_options(self, template_config: Optional[Dict], 
                      user_options: Optional[Dict]) -> Dict:
        """合并转换选项"""
        options = {}
        
        # 默认选项
        default_options = {
            'toc': False,
            'number_sections': False,
            'highlight_style': 'github',
            'font_size': '12pt',
            'margin': '2cm'
        }
        
        options.update(default_options)
        
        # 模板配置
        if template_config:
            options.update(template_config)
        
        # 用户选项
        if user_options:
            options.update(user_options)
        
        return options
    
    def _generate_output_path(self, task_id: str, output_format: str) -> str:
        """生成输出文件路径"""
        output_dir = self.app.config.get('OUTPUT_FOLDER', 'storage/outputs') if self.app else 'storage/outputs'
        filename = f"{task_id}.{output_format}"
        return os.path.join(output_dir, filename)
    
    def get_supported_formats(self) -> List[str]:
        """获取支持的输出格式列表"""
        return self.supported_formats.copy()
    
    def validate_conversion_options(self, output_format: str, options: Dict) -> Dict[str, Any]:
        """验证转换选项"""
        result = {'valid': True, 'errors': []}
        
        if output_format not in self.supported_formats:
            result['valid'] = False
            result['errors'].append(f"不支持的输出格式: {output_format}")
        
        # 验证具体选项
        if options.get('font_size'):
            if not ValidationUtils.validate_css_size(options['font_size']):
                result['errors'].append("无效的字体大小格式")
        
        if options.get('margin'):
            if not ValidationUtils.validate_css_size(options['margin']):
                result['errors'].append("无效的边距格式")
        
        if result['errors']:
            result['valid'] = False
        
        return result
    
    def estimate_conversion_time(self, file_size: int, output_format: str) -> int:
        """估算转换时间（秒）"""
        # 基础时间（秒）
        base_time = 2
        
        # 根据文件大小调整（每KB增加0.1秒）
        size_factor = file_size / 1024 * 0.1
        
        # 根据输出格式调整
        format_multiplier = {
            'pdf': 1.5,
            'docx': 1.2,
            'html': 0.8,
            'epub': 1.3,
            'rtf': 1.0,
            'odt': 1.1
        }
        
        multiplier = format_multiplier.get(output_format, 1.0)
        
        return int(base_time + size_factor * multiplier)